package View;

import Model.BeingActionEvent;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Observable;
import java.util.Observer;
import javax.swing.ImageIcon;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.Timer;

public class BoardView extends JPanel implements ActionListener, Observer {

    private BoardMap map;
    private Camera camera;
    private Timer timer;

    public BoardView() {
        this.map = new BoardMap();
        this.camera = new Camera(0, 0);
        this.map.initDemoMap();

        timer = new Timer(25, this);
        timer.start();

        loadContent();
        initComponents();
    }

    public BoardView(BoardMap boardMap) {
        this.map = boardMap;
        this.camera = new Camera(0, 0);
        //this.map.initDemoMap();

        loadContent();
        initComponents();
    }

    private void loadContent() {
        ImageIcon ii = new ImageIcon(this.getClass().getResource("../images/part4_tileset_modif.png"));
        Tile.TileSetTexture = ii.getImage();

        ImageIcon cc = new ImageIcon(this.getClass().getResource("../images/characters_64.png"));
        BeingView.characterTexture = cc.getImage();

        ImageIcon qg = new ImageIcon(this.getClass().getResource("../images/qg.png"));
        Tile.qgTile = qg.getImage();

    }

    private void initComponents() {
        setBackground(Color.BLACK);

        this.getInputMap().put(KeyStroke.getKeyStroke("LEFT"), "moveCameraLeft");
        this.getInputMap().put(KeyStroke.getKeyStroke("RIGHT"), "moveCameraRight");
        this.getInputMap().put(KeyStroke.getKeyStroke("UP"), "moveCameraUp");
        this.getInputMap().put(KeyStroke.getKeyStroke("DOWN"), "moveCameraDown");

        this.getActionMap().put("moveCameraLeft", new KeyAction("LEFT", this));
        this.getActionMap().put("moveCameraRight", new KeyAction("RIGHT", this));
        this.getActionMap().put("moveCameraUp", new KeyAction("UP", this));
        this.getActionMap().put("moveCameraDown", new KeyAction("DOWN", this));
    }

    public void paint(Graphics g) {
        super.paint(g);
        Graphics2D g2d = (Graphics2D) g;

        int firstSquare[] = {camera.positionX / Tile.TileStepX, camera.positionY / Tile.TileStepY};
        int firstX = firstSquare[0];
        int firstY = firstSquare[1];

        int squareOffset[] = {camera.positionX % Tile.TileStepX, camera.positionY % Tile.TileStepY};
        int offsetX = (int) squareOffset[0];
        int offsetY = (int) squareOffset[1];

        for (int y = 0; y < camera.squaresDown; y++) {
            int rowOffset = 0;
            if ((firstY + y) % 2 == 1) {
                rowOffset = Tile.OddRowXOffset;
            }

            for (int x = 0; x < camera.squaresAcross; x++) {
                int mapx = (firstX + x);
                int mapy = (firstY + y);

                if (mapx < map.mapWidth && mapy < map.mapHeight) {

                    //DRAW BASE TILE
                    int tileID = map.map[mapx][mapy].getBaseTile(); //init les squareview
                    //get Texture from id
                    int[] textureUpperLeftXY = Tile.GetUpperLeftXY(tileID);

                    int dstx1 = (x * Tile.TileStepX) - offsetX + rowOffset + camera.baseOffsetX;
                    int dsty1 = (y * Tile.TileStepY) - offsetY + camera.baseOffsetY;
                    int dstx2 = dstx1 + Tile.TileWidth;
                    int dsty2 = dsty1 + Tile.TileHeight;

                    int srcx1 = textureUpperLeftXY[0];
                    int srcy1 = textureUpperLeftXY[1];
                    int srcx2 = srcx1 + Tile.TileWidth;
                    int srcy2 = srcy1 + Tile.TileHeight;

                    g2d.drawImage(Tile.TileSetTexture, dstx1 * camera.cameraZoom / camera.zoomAmplitude, dsty1 * camera.cameraZoom / camera.zoomAmplitude, dstx2 * camera.cameraZoom / camera.zoomAmplitude, dsty2 * camera.cameraZoom / camera.zoomAmplitude, srcx1, srcy1, srcx2, srcy2, this);


                    //DRAW HEIGHT TILES
                    int heightRow = 0;

                    for (int heightTileID : map.map[mapx][mapy].getHeightTiles()) {
                        //get Texture from heightTileID
                        textureUpperLeftXY = Tile.GetUpperLeftXY(heightTileID);

                        dstx1 = (x * Tile.TileStepX) - offsetX + rowOffset + camera.baseOffsetX;
                        dsty1 = (y * Tile.TileStepY) - offsetY + camera.baseOffsetY - (heightRow * Tile.HeightTileOffset);
                        dstx2 = dstx1 + Tile.TileWidth;
                        dsty2 = dsty1 + Tile.TileHeight;

                        srcx1 = textureUpperLeftXY[0];
                        srcy1 = textureUpperLeftXY[1];
                        srcx2 = srcx1 + Tile.TileWidth;
                        srcy2 = srcy1 + Tile.TileHeight;

                        g2d.drawImage(Tile.TileSetTexture, dstx1 * camera.cameraZoom / camera.zoomAmplitude, dsty1 * camera.cameraZoom / camera.zoomAmplitude, dstx2 * camera.cameraZoom / camera.zoomAmplitude, dsty2 * camera.cameraZoom / camera.zoomAmplitude, srcx1, srcy1, srcx2, srcy2, this);

                        heightRow++;
                    }

                    if (map.map[mapx][mapy].getCharacter() != -1) {
                        int[] characterSprite = BeingView.GetUpperLeftXY(map.map[mapx][mapy].getCharacter());

                        dstx1 = (x * Tile.TileStepX) - offsetX + rowOffset + camera.baseOffsetX;
                        dsty1 = (y * Tile.TileStepY) - offsetY + camera.baseOffsetY;
                        dstx2 = dstx1 + BeingView.characterWidth;
                        dsty2 = dsty1 + BeingView.characterHeight;

                        srcx1 = characterSprite[0];
                        srcy1 = characterSprite[1];
                        srcx2 = srcx1 + BeingView.characterWidth;
                        srcy2 = srcy1 + BeingView.characterHeight;

                        g2d.drawImage(BeingView.characterTexture, dstx1 * camera.cameraZoom / camera.zoomAmplitude, dsty1 * camera.cameraZoom / camera.zoomAmplitude, dstx2 * camera.cameraZoom / camera.zoomAmplitude, dsty2 * camera.cameraZoom / camera.zoomAmplitude, srcx1, srcy1, srcx2, srcy2, this);

                    }

                    if (map.map[mapx][mapy].getBuilding() != -1) {
                        int[] buildingSprite = BeingView.GetUpperLeftXY(map.map[mapx][mapy].getBuilding());

                        dstx1 = (x * Tile.TileStepX) - offsetX + rowOffset + camera.baseOffsetX;
                        dsty1 = (y * Tile.TileStepY) - offsetY + camera.baseOffsetY;
                        dstx2 = dstx1 + Tile.TileWidth;
                        dsty2 = dsty1 + Tile.TileHeight;

                        srcx1 = buildingSprite[0];
                        srcy1 = buildingSprite[1];
                        srcx2 = srcx1 + Tile.TileWidth;
                        srcy2 = srcy1 + Tile.TileHeight;

                        g2d.drawImage(Tile.qgTile, dstx1 * camera.cameraZoom / camera.zoomAmplitude, dsty1 * camera.cameraZoom / camera.zoomAmplitude, dstx2 * camera.cameraZoom / camera.zoomAmplitude, dsty2 * camera.cameraZoom / camera.zoomAmplitude, srcx1, srcy1, srcx2, srcy2, this);

                    }

                    if (camera.showXY && camera.cameraZoom > 4) {
                        g2d.drawString("( " + mapx + "," + mapy + " ) ", (dstx1 + 16) * camera.cameraZoom / camera.zoomAmplitude, (dsty1 + 56) * camera.cameraZoom / camera.zoomAmplitude);
                    }
                }
            }
        }
    }

    public void actionPerformed(ActionEvent e) {
        repaint();
    }

    public Camera getCamera() {
        return camera;
    }

    public BoardMap getMap() {
        return map;
    }

    public void update(Observable observable, Object action) {

        try {
            BeingActionEvent bae = (BeingActionEvent) action;
            if (bae.getActionType().equals("Move")) {
                this.updateBeingPosition(bae);
                this.repaint();
            } else if (bae.getActionType().equals("Kill")) {
                this.updateBeingKill(bae);
                this.repaint();
            } else if (bae.getActionType().equals("Pickup")) {
                this.updateBeingPickup(bae);
                this.repaint();
            } else if (bae.getActionType().equals("Drop")) {
                this.updateBeingDrop(bae);
                this.repaint();
            } else if (bae.getActionType().equals("LionKill")) {
                this.updateLionKill(bae);
                this.repaint();
            }
        } catch (Exception e) {
            System.out.printf("Invalid being action event --BoardView");
            e.printStackTrace();
        }

    }

    private void updateBeingKill(BeingActionEvent bae) {
        //set Being with ressource
        this.map.map[bae.getBeingPosition().getXPosition()][bae.getBeingPosition().getYPosition()].setCharacter(16);
        //kill target
        switch (bae.getAnimalType()) {
            case Doe:
                this.map.map[bae.getTargetPosition().getXPosition()][bae.getTargetPosition().getYPosition()].setCharacter(17);
                break;
            case Elephant:
                this.map.map[bae.getTargetPosition().getXPosition()][bae.getTargetPosition().getYPosition()].setCharacter(13);
                break;
        }


        //if target has no food, remove from screen
        if (bae.isRemoveTarget()) {
            this.map.map[bae.getTargetPosition().getXPosition()][bae.getTargetPosition().getYPosition()].setCharacter(-1);
        }

    }

    private void updateBeingPickup(BeingActionEvent bae) {
        switch (bae.getBeingActivity()) {
            case Goldminer:
                //set Being with ressource
                this.map.map[bae.getBeingPosition().getXPosition()][bae.getBeingPosition().getYPosition()].setCharacter(2);
                //remove resource
                if (bae.isRemoveTarget()) {
                    this.map.map[bae.getTargetPosition().getXPosition()][bae.getTargetPosition().getYPosition()].setBaseTile(1);
                }

                break;
            case Stoneminer:
                //set Being with ressource
                this.map.map[bae.getBeingPosition().getXPosition()][bae.getBeingPosition().getYPosition()].setCharacter(3);
                //remove resource
                if (bae.isRemoveTarget()) {
                    this.map.map[bae.getTargetPosition().getXPosition()][bae.getTargetPosition().getYPosition()].modifyTopTile(-1);
                }

                break;
            case Lumberjack:
                //set Being with ressource
                this.map.map[bae.getBeingPosition().getXPosition()][bae.getBeingPosition().getYPosition()].setCharacter(3);
                //cut tree
                this.map.map[bae.getTargetPosition().getXPosition()][bae.getTargetPosition().getYPosition()].modifyTopTile(125);
                //if no wood on the tree remove it from screen
                if (bae.isRemoveTarget()) {
                    this.map.map[bae.getTargetPosition().getXPosition()][bae.getTargetPosition().getYPosition()].modifyTopTile(-1);
                }
                break;
        }
    }

    private void updateBeingPosition(BeingActionEvent bae) {
        int characterID = this.map.map[bae.getTargetPosition().getXPosition()][bae.getTargetPosition().getYPosition()].getCharacter();
        this.map.map[bae.getTargetPosition().getXPosition()][bae.getTargetPosition().getYPosition()].setCharacter(-1);
        this.map.map[bae.getBeingPosition().getXPosition()][bae.getBeingPosition().getYPosition()].setCharacter(characterID);
    }

    private void updateBeingDrop(BeingActionEvent bae) {
        switch (bae.getBeingActivity()) {
            case Goldminer:
                this.map.map[bae.getBeingPosition().getXPosition()][bae.getBeingPosition().getYPosition()].setCharacter(9);
                break;
            case Stoneminer:
                this.map.map[bae.getBeingPosition().getXPosition()][bae.getBeingPosition().getYPosition()].setCharacter(9);
                break;
            case Lumberjack:
                this.map.map[bae.getBeingPosition().getXPosition()][bae.getBeingPosition().getYPosition()].setCharacter(7);
                break;
            case Hunter:
                this.map.map[bae.getBeingPosition().getXPosition()][bae.getBeingPosition().getYPosition()].setCharacter(6);
                break;
        }
    }

    private void updateLionKill(BeingActionEvent bae) {

        this.map.map[bae.getTargetPosition().getXPosition()][bae.getTargetPosition().getYPosition()].setCharacter(-1);

    }
}
